﻿// Decompiled with JetBrains decompiler
// Type: Netick.NetworkQueue`1
// Assembly: Netick, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 6726ECA1-C773-4CF7-8952-E81B30D93B52
// Assembly location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.dll
// XML documentation location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.xml

using System;
using System.Collections;
using System.Collections.Generic;

#nullable disable
namespace Netick;

/// <summary>
/// A networked <see cref="T:System.Collections.Generic.Queue`1" /> collection.
/// </summary>
public sealed class NetworkQueue<T> : NetworkQueue, INetworkCollection, IEnumerable<T>, IEnumerable where T : unmanaged
{
  private Queue<T> _original;

  public NetworkQueue(int capacity)
  {
    this._length = capacity;
    this._original = new Queue<T>(capacity);
  }

  public override unsafe void InternalInit(
    INetickNetworkScript beh,
    int* state,
    int elementSizeWords,
    int flags)
  {
    base.InternalInit(beh, state, elementSizeWords, flags);
    this._elementSizeWords = NetickUtils.GetWordSize(sizeof (T));
  }

  public IEnumerator<T> GetEnumerator()
  {
    NetworkQueue<T> networkQueue = this;
    int h = networkQueue.Head;
    for (int i = 0; i < networkQueue.Count; ++i)
    {
      yield return networkQueue.GetElementDataPtr(h);
      h = (h + 1) % networkQueue._length;
    }
  }

  IEnumerator IEnumerable.GetEnumerator() => (IEnumerator) this.GetEnumerator();

  internal unsafe void SetElementData(int index, T value)
  {
    this._entity.SetState_Collection((int*) &value, this._intS + 3 + index * this._elementSizeWords, this._elementSizeWords, 0, (this._flags & NetworkCollectionMeta.IsInputSourceOnly) == NetworkCollectionMeta.IsInputSourceOnly ? 1 : 0, (this._flags & NetworkCollectionMeta.HasOnChanged) == NetworkCollectionMeta.HasOnChanged ? 1 : 0);
  }

  internal unsafe T GetElementDataPtr(int index)
  {
    return *(T*) (this._intS + 3 + index * this._elementSizeWords);
  }

  /// <summary>Gets the number of elements contained in the queue.</summary>
  public unsafe int Count
  {
    get => (IntPtr) this._intS == IntPtr.Zero ? this._original.Count : *this._intS;
    internal set
    {
      this._entity.SetState_Collection(&value, this._intS, 1, (this._flags & NetworkCollectionMeta.HasOnChanged) == NetworkCollectionMeta.HasOnChanged ? 1 : 0, (this._flags & NetworkCollectionMeta.IsInputSourceOnly) == NetworkCollectionMeta.IsInputSourceOnly ? 1 : 0, (this._flags & NetworkCollectionMeta.HasOnChanged) == NetworkCollectionMeta.HasOnChanged ? 1 : 0);
    }
  }

  private unsafe int Head
  {
    get => (IntPtr) this._intS == IntPtr.Zero ? this._original.Count : this._intS[1];
    set
    {
      this._entity.SetState_Collection(&value, this._intS + 1, 1, 0, (this._flags & NetworkCollectionMeta.IsInputSourceOnly) == NetworkCollectionMeta.IsInputSourceOnly ? 1 : 0, (this._flags & NetworkCollectionMeta.HasOnChanged) == NetworkCollectionMeta.HasOnChanged ? 1 : 0);
    }
  }

  private unsafe int Tail
  {
    get => (IntPtr) this._intS == IntPtr.Zero ? this._original.Count : this._intS[2];
    set
    {
      this._entity.SetState_Collection(&value, this._intS + 2, 1, 0, (this._flags & NetworkCollectionMeta.IsInputSourceOnly) == NetworkCollectionMeta.IsInputSourceOnly ? 1 : 0, (this._flags & NetworkCollectionMeta.HasOnChanged) == NetworkCollectionMeta.HasOnChanged ? 1 : 0);
    }
  }

  /// <summary>Removes all elements from the queue.</summary>
  public void Clear()
  {
    this.Count = 0;
    this.Head = 0;
    this.Tail = 0;
  }

  /// <summary>
  /// Adds an item to the end of the queue if there is capacity.
  /// </summary>
  /// <param name="item">The item to add.</param>
  /// <returns><c>true</c> if the item was added; otherwise, <c>false</c> if the queue is full.</returns>
  public bool Enqueue(T item)
  {
    if (this.Count >= this._length)
      return false;
    int tail = this.Tail;
    this.Tail = (this.Tail + 1) % this._length;
    this.SetElementData(tail, item);
    ++this.Count;
    return true;
  }

  /// <summary>
  /// Removes and returns the item at the beginning of the queue.
  /// </summary>
  /// <returns>The item removed from the beginning of the queue.</returns>
  /// <exception cref="T:System.InvalidOperationException">Thrown if the queue is empty.</exception>
  public T Dequeue()
  {
    if (this.Count <= 0)
      throw new InvalidOperationException("The queue is empty.");
    int head = this.Head;
    this.Head = (this.Head + 1) % this._length;
    --this.Count;
    return this.GetElementDataPtr(head);
  }

  /// <summary>
  /// Attempts to remove and return the item at the beginning of the queue.
  /// </summary>
  /// <param name="result">When this method returns, contains the object removed from the queue, or the default value if the queue is empty.</param>
  /// <returns><c>true</c> if an item was dequeued; otherwise, <c>false</c>.</returns>
  public bool TryDequeue(out T result)
  {
    if (this.Count > 0)
    {
      int head = this.Head;
      this.Head = (head + 1) % this._length;
      --this.Count;
      result = this.GetElementDataPtr(head);
      return true;
    }
    result = default (T);
    return false;
  }

  /// <summary>
  /// Returns the item at the beginning of the queue without removing it.
  /// </summary>
  /// <returns>The item at the beginning of the queue.</returns>
  /// <exception cref="T:System.InvalidOperationException">Thrown if the queue is empty.</exception>
  public T Peek()
  {
    if (this.Count <= 0)
      throw new InvalidOperationException("The queue is empty.");
    return this.GetElementDataPtr(this.Head);
  }

  /// <summary>
  /// Attempts to return the item at the beginning of the queue without removing it.
  /// </summary>
  /// <param name="result">When this method returns, contains the object at the beginning of the queue, or the default value if the queue is empty.</param>
  /// <returns><c>true</c> if an item was found; otherwise, <c>false</c>.</returns>
  public bool TryPeek(out T result)
  {
    if (this.Count > 0)
    {
      result = this.GetElementDataPtr(this.Head);
      return true;
    }
    result = default (T);
    return false;
  }

  /// <summary>
  /// Adds an item to the underlying original queue. Used for local operations.
  /// </summary>
  /// <param name="item">The item to add.</param>
  public void Add(T item) => this._original.Enqueue(item);

  public override unsafe void InternalReset()
  {
    *this._intS = 0;
    this._intS[1] = 0;
    this._intS[2] = 0;
    for (int index = 0; index < this._length; ++index)
      this.SetElementData(index, default (T));
    int num = 0;
    foreach (T obj in this._original)
    {
      this.Enqueue(obj);
      ++num;
    }
    this.Count = this._original.Count;
  }
}
